Phần mềm vẽ đồ thị C#
6.577 lượt xem;
- ExpressionPlotter.cs
- project /
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Data;
5 using System.Drawing;
6 using System.Text;
7 using System.Windows.Forms;
8 using System.Drawing.Printing;
9
10 namespace VietGraph
11 {
12 public enum GraphMode
13 { Rectangular, Polar };
14
15 //[ToolboxBitmap("graph.bmp")]
16 public partial class ExpressionPlotter : Control
17 {
18
19 #region MemberVariables
20
21 int iLengthScale; //represents no. of pixels for a scale
22 int iOriginX, iOriginY; //represents the location of origin
23
24 double dScaleX = 10, dScaleY = 10; //base scale for graph
25 double dForwardX = 0, dForwardY = 0; //position related to base scale
26
27 int iDivisionsX = 5, iDivisionsY = 5;
28
29 int iLengthBox;
30 int iPrintStepX = 1;
31 int iPrintStepY = 1;
32 int iControlSize = 0;
33 int iPenWidth = 1;
34
35 bool bGrids = false;
36 bool bDisplayText = true;
37
38 int iPolarSensitivity = 100;
39
40 GraphMode graphMode = GraphMode.Rectangular;
41 PrintDocument printDoc;
42
43 List<IEvaluatable> expressions;
44 List<Color> expColors;
45 List<bool> expVisible;
46
47 #endregion
48
49 #region Control specific functions
50 public ExpressionPlotter()
51 {
52 this.expressions = new List<IEvaluatable>();
53 this.expColors = new List<Color>();
54 this.expVisible = new List<bool>();
55 InitializeComponent();
56 }
57
58 protected override void OnPaint(PaintEventArgs pe)
59 {
60 //update internal variables
61 UpdateVariables();
62
63 pe.Graphics.Clear(Color.White);
64 pe.Graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
65 pe.Graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
66 pe.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
67 PlotGraph(pe.Graphics);
68
69 // Calling the base class OnPaint
70 base.OnPaint(pe);
71 }
72
73 //private void ExpressionPlotter_Resize(object sender, EventArgs e)
74 //{
75 // //code to keep the control's height and width same
76
77 // //see whether width was changed or the height was changed
78 // if (iControlSize != this.Width)
79 // this.Height = this.Width; //width was changed so adjust height accordingly
80 // else
81 // this.Width = this.Height; //height was changed so adjust width accordingly
82 //}
83
84 private void UpdateVariables()
85 {
86 iControlSize = this.Height;
87 iLengthScale = (int)(iControlSize / 2.25);
88 iOriginX = (this.Width) / 2;
89 iOriginY = (this.Height) / 2;
90 if (bGrids == true)
91 this.iLengthBox = this.iLengthScale;
92 else
93 this.iLengthBox = (int)(this.iLengthScale * 0.025);
94 }
95 private double IncreaseScale(double scale)
96 {
97 double absScale = Math.Round(Math.Abs(scale), 3);
98 double newScale;
99 if (absScale >= 100)
100 newScale = (absScale + 100);
101 else if (absScale >= 10)
102 newScale = (absScale + 10);
103 else if (absScale >= 1)
104 newScale = (absScale + 1);
105 else if (absScale >= .10)
106 newScale = (absScale + .10);
107 else
108 newScale = (absScale + .010);
109 return newScale * Math.Sign(scale);
110 }
111 private double DecreaseScale(double scale)
112 {
113 double absScale = Math.Round(Math.Abs(scale), 3);
114 double newScale;
115 if (absScale > 100)
116 newScale = (absScale - 100);
117 else if (absScale > 10)
118 newScale = (absScale - 10);
119 else if (absScale > 1)
120 newScale = (absScale - 1);
121 else if (absScale > .1)
122 newScale = (absScale - .1);
123 else if (absScale > .01)
124 newScale = (absScale - .01);
125 else
126 newScale = absScale;
127 return newScale * Math.Sign(scale);
128 }
129
130 #endregion
131
132 #region Properties
133 public int DivisionsX
134 {
135 get { return this.iDivisionsX; }
136 set { if (value > 0) this.iDivisionsX = value; }
137 }
138 public int DivisionsY
139 {
140 get { return this.iDivisionsY; }
141 set { if (value > 0) this.iDivisionsY = value; }
142 }
143
144 public double ScaleX
145 {
146 get { return this.dScaleX; }
147 set { if (value != 0) this.dScaleX = value; }
148 }
149 public double ScaleY
150 {
151 get { return this.dScaleY; }
152 set { if (value != 0) this.dScaleY = value; }
153 }
154
155 public double ForwardX
156 {
157 get { return this.dForwardX; }
158 set { this.dForwardX = value; }
159 }
160 public double ForwardY
161 {
162 get { return this.dForwardY; }
163 set { this.dForwardY = value; }
164 }
165
166 public int PrintStepX
167 {
168 get { return this.iPrintStepX; }
169 set { if (value > 0) this.iPrintStepX = value; }
170 }
171 public int PrintStepY
172 {
173 get { return this.iPrintStepY; }
174 set { if (value > 0) this.iPrintStepY = value; }
175 }
176
177 public int PolarSensitivity
178 {
179 get { return this.iPolarSensitivity; }
180 set { if (value > 0) this.iPolarSensitivity = value; }
181 }
182
183 public bool Grids
184 {
185 get { return this.bGrids; }
186 set { this.bGrids = value; }
187 }
188
189 public bool DisplayText
190 {
191 get { return bDisplayText; }
192 set { bDisplayText = value; }
193 }
194
195 public GraphMode GraphMode
196 {
197 get { return graphMode; }
198 set { graphMode = value; }
199 }
200
201 public int PenWidth
202 {
203 get { return iPenWidth; }
204 set { iPenWidth = value; }
205 }
206
207 #endregion
208
209 #region Plotting Functions
210 void PlotGraph(Graphics g)
211 {
212 DisplayScale(g);
213 if (this.bDisplayText)
214 DisplayExpressionsText(g);
215
216 double X, Y;
217 double dPointX, dPointY;
218 double dLeastStepX, dLeastStepY;
219 double dMin, dMax, dStep;
220 int i;
221
222 //(X1,Y1) is the previous point ploted, (X2,Y2) is the current point to plot. (we will join both to have our
223 // graph continuous).
224 float X1 = 0, Y1 = 0, X2 = 0, Y2 = 0;
225 //This variable controls whether our graph should be continuous or not
226 bool bContinuity = false;
227
228 //divide scale with its length(pixels) to get increment per pixel
229 dLeastStepX = dScaleX / iLengthScale;
230 dLeastStepY = dScaleY / iLengthScale;
231
232 //prepare variables for loop
233 if (graphMode == GraphMode.Polar)
234 {
235 dMin = -Math.PI;
236 dMax = Math.PI;
237 dStep = dScaleX / iPolarSensitivity;
238 }
239 else //if (Rectangular Mode)
240 {
241 dStep = dLeastStepX;
242 dMin = -dScaleX + dForwardX;
243 dMax = dScaleX + dForwardX;
244 }
245
246
247 for (i = 0; i < this.expressions.Count; i++)
248 {
249 //check if expression needs to be drawn and is valid
250 if (expVisible[i] == true && expressions[i].IsValid == true)
251 {
252 bContinuity = false;
253 for (X = dMin; X != dMax; X += dStep)
254 {
255 if (dScaleX < 0 && X < dMax)
256 break;
257 if (dScaleX > 0 && X > dMax)
258 break;
259 try
260 {
261 //evaluate expression[i] at point: X
262 Y = expressions[i].Evaluate(X);
263 if (double.IsNaN(Y))
264 {
265 //break continuity in graph if expression returned a NaN
266 bContinuity = false;
267 continue;
268 }
269
270 //get points to plot
271 if (graphMode == GraphMode.Polar)
272 {
273 dPointX = Y * Math.Cos(X) / dLeastStepX;
274 dPointY = Y * Math.Sin(X) / dLeastStepY;
275 }
276 else // if (Rectangular mode;
277 {
278 dPointX = X / dLeastStepX;
279 dPointY = Y / dLeastStepY;
280 }
281
282 //check if the point to be plotted lies inside our visible area(i.e. inside our current axes ranges)
283 if ((iOriginY - dPointY + dForwardY / dLeastStepY) < iOriginY - iLengthScale
284 || (iOriginY - dPointY + dForwardY / dLeastStepY) > iOriginY + iLengthScale
285 || (iOriginX + dPointX - dForwardX / dLeastStepX) < iOriginX - iLengthScale
286 || (iOriginX + dPointX - dForwardX / dLeastStepX) > iOriginX + iLengthScale)
287 {
288 //the point lies outside our current scale so break continuity
289 bContinuity = false;
290 continue;
291 }
292
293 //get coordinates for currently evaluated point
294 X2 = (float)(iOriginX + dPointX - dForwardX / dLeastStepX);
295 Y2 = (float)(iOriginY - dPointY + dForwardY / dLeastStepY);
296
297 //if graph should not be continuous
298 if (bContinuity == false)
299 {
300 X1 = X2;
301 Y1 = Y2;
302 // the graph should be continuous afterwards since the current evaluated value is valid
303 // and can be plot within our axes range
304 bContinuity = true;
305
306 }
307
308 //join points (X1,Y1) and (X2,Y2)
309 g.DrawLine(new Pen(expColors[i], iPenWidth), new PointF(X1, Y1), new PointF(X2, Y2));
310
311 //get current values into X1,Y1
312 X1 = X2;
313 Y1 = Y2;
314 }
315 catch
316 {
317 bContinuity = false;
318 continue;
319 }
320
321 }
322 }
323 }
324 }
325
326 void DisplayScale(Graphics g)
327 {
328 //axes lines
329 g.DrawLine(new Pen(Color.Black, 2),
330 new Point(iOriginX - iLengthScale, iOriginY),
331 new Point(iOriginX + iLengthScale, iOriginY));
332
333 g.DrawLine(new Pen(Color.Black, 2),
334 new Point(iOriginX, iOriginY - iLengthScale),
335 new Point(iOriginX, iOriginY + iLengthScale));
336
337 int i;
338 double dValue;
339 string strValue;
340
341 float cordX, cordY;
342
343 //X-axis values
344 dValue = -dScaleX + dForwardX;
345 for (i = -iDivisionsX; i <= iDivisionsX; i++)
346 {
347 g.DrawLine(new Pen(Color.Gray, 1),
348 new PointF((float)(iOriginX + (dValue - dForwardX) * iLengthScale / dScaleX), iOriginY - iLengthBox),
349 new PointF((float)(iOriginX + (dValue - dForwardX) * iLengthScale / dScaleX), iOriginY + iLengthBox));
350 if (i % iPrintStepX == 0 && i != 0)
351 {
352 strValue = Math.Round(dValue, 3).ToString();
353 cordX = (float)(iOriginX + (dValue - dForwardX) * iLengthScale / dScaleX - 6 - (strValue.Length - 2) * 5);
354 cordY = (float)(iOriginY + 10);
355 g.DrawString(strValue, new Font("Arial", 8), new SolidBrush(Color.Black), cordX, cordY);
356 }
357 dValue = dValue + dScaleX / iDivisionsX;
358 }
359
360
361 //Y-axis values
362 dValue = -dScaleY + dForwardY;
363 for (i = -iDivisionsY; i <= iDivisionsY; i++)
364 {
365 g.DrawLine(new Pen(Color.Gray, 1),
366 new PointF(iOriginX - iLengthBox, (float)(iOriginY + (dValue - dForwardY) * iLengthScale / dScaleY)),
367 new PointF(iOriginX + iLengthBox, (float)(iOriginY + (dValue - dForwardY) * iLengthScale / dScaleY)));
368 if (i % iPrintStepY == 0 && i != 0)
369 {
370 strValue = Math.Round(dValue, 3).ToString();
371 cordX = (float)(iOriginX - 20 - (strValue.Length) * 4);
372 cordY = (float)(iOriginY - (dValue - dForwardY) * iLengthScale / dScaleY - 7);
373 if (this.iLengthBox == this.iLengthScale)
374 cordY += 6;
375 g.DrawString(strValue, new Font("Arial", 8), new SolidBrush(Color.Black), cordX, cordY);
376 }
377 dValue = dValue + dScaleY / iDivisionsY;
378 }
379
380 if (graphMode == GraphMode.Polar)
381 {
382 g.DrawEllipse(new Pen(Color.Black, 1), iOriginX - iLengthScale, iOriginY - (float)(iLengthScale * dScaleX / dScaleY), iLengthScale*2, (float)(iLengthScale * dScaleX / dScaleY)*2);
383 for (dValue = 0; dValue <= 2 * Math.PI; dValue += Math.PI / 6)
384 {
385 g.DrawLine(new Pen(Color.Black, 1), new Point(iOriginX, iOriginY),
386 new PointF((float)(iOriginX + iLengthScale * Math.Cos(dValue)), (float)(iOriginY + iLengthScale * Math.Sin(dValue))));
387 }
388 }
389 }
390
391 void DisplayExpressionsText(Graphics g)
392 {
393 int line = 0;
394 for (int i = 0; i < this.expressions.Count; i++)
395 {
396 if (expVisible[i] == true)
397 {
398 if (expressions[i].IsValid)
399 g.DrawString(expressions[i].ExpressionText, new Font("Arial", 10), new SolidBrush(expColors[i]), 10, 10 + 18 * line);
400 else
401 g.DrawString("ERROR: " + expressions[i].ExpressionText, new Font("Arial", 10), new SolidBrush(expColors[i]), 10, 10 + 18 * line);
402 line++;
403 }
404 }
405 }
406
407 void InitializePrintDoc()
408 {
409 this.printDoc = new PrintDocument();
410 this.printDoc.OriginAtMargins = true;
411 this.printDoc.DefaultPageSettings.Margins.Left = 200;
412 this.printDoc.DefaultPageSettings.Margins.Top = 100;
413 this.printDoc.DocumentName = "Graph Plotter by Syed Mehroz Alam";
414 this.printDoc.PrintPage += delegate(object sender, PrintPageEventArgs e) { PlotGraph(e.Graphics); };
415 }
416
417 #endregion
418
419 #region Public functions for expression management
420
421 public void AddExpression(IEvaluatable expression, Color color, bool visible)
422 {
423 expressions.Add(expression);
424 expColors.Add(color);
425 expVisible.Add(visible);
426 }
427 public bool RemoveExpression(IEvaluatable expression)
428 {
429 int index = expressions.IndexOf(expression);
430 if (index == -1)
431 return false;
432 expressions.RemoveAt(index);
433 expColors.RemoveAt(index);
434 expVisible.RemoveAt(index);
435 return true;
436 }
437 public void RemoveExpressionAt(int index)
438 {
439 // can throw OutOfRangeException
440 expressions.RemoveAt(index);
441 expColors.RemoveAt(index);
442 expVisible.RemoveAt(index);
443 }
444 public void RemoveAllExpressions()
445 {
446 this.expressions.Clear();
447 this.expColors.Clear();
448 this.expVisible.Clear();
449 }
450
451 public IEvaluatable GetExpression(int index)
452 {
453 // can throw OutOfRangeException
454 return expressions[index];
455 }
456 public Color GetExpressionColor(int index)
457 {
458 // can throw OutOfRangeException
459 return expColors[index];
460 }
461 public bool GetExpressionVisibility(int index)
462 {
463 // can throw OutOfRangeException
464 return expVisible[index];
465 }
466
467 public void SetExpression(int index, IEvaluatable expression)
468 {
469 // can throw OutOfRangeException
470 this.expressions[index]=expression;
471 }
472 public void SetExpressionColor(int index, Color color)
473 {
474 // can throw OutOfRangeException
475 this.expColors[index]=color;
476 }
477 public void SetExpressionVisibility(int index, bool visibility)
478 {
479 // can throw OutOfRangeException
480 this.expVisible[index]=visibility;
481 }
482
483 #endregion
484
485 #region Public functions for graph management
486
487 public void SetRangeX(double startX, double endX)
488 {
489 this.dScaleX = (endX - startX) / 2;
490 this.dForwardX = (endX + startX) / 2;
491 }
492 public void SetRangeY(double startY, double endY)
493 {
494 this.dScaleY = (endY - startY) / 2;
495 this.dForwardY = (endY + startY) / 2;
496 }
497
498 public void RestoreDefaults()
499 {
500 this.dScaleX = this.dScaleY = 10;
501 this.dForwardX = this.dForwardY = 0;
502 this.iDivisionsX=this.iDivisionsY = 5;
503 this.iPrintStepX = this.iPrintStepY = 1;
504 this.bGrids = false;
505 this.iPolarSensitivity = 100;
506 }
507
508 public void ToggleGrids()
509 {
510 this.bGrids = (!bGrids);
511 }
512
513 public double[] GetValues(double x)
514 {
515 double[] result = new double[expressions.Count];
516 for (int i = 0; i < this.expressions.Count; i++)
517 if (this.expressions[i].IsValid)
518 result[i] = this.expressions[i].Evaluate(x);
519 return result;
520 }
521
522 public Bitmap GetGraphBitmap()
523 {
524 Bitmap bmpSnap = new Bitmap(this.Width, this.Height);
525 DrawToBitmap(bmpSnap, new Rectangle(0, 0, this.Width, this.Height));
526 return bmpSnap;
527 }
528
529 public void CopyToClipboard()
530 {
531 Clipboard.SetImage(GetGraphBitmap());
532 }
533
534 public void MoveLeft(int divisions)
535 {
536 this.dForwardX -= divisions * this.dScaleX / this.iDivisionsX;
537 }
538 public void MoveRight(int divisions)
539 {
540 this.dForwardX += divisions * this.dScaleX / this.iDivisionsX;
541 }
542 public void MoveUp(int divisions)
543 {
544 this.dForwardY += divisions * this.dScaleY / this.iDivisionsY;
545 }
546 public void MoveDown(int divisions)
547 {
548 this.dForwardY -= divisions * this.dScaleY / this.iDivisionsY;
549 }
550
551 public void ZoomInX()
552 {
553 this.dScaleX = DecreaseScale(this.dScaleX);
554 }
555 public void ZoomInY()
556 {
557 this.dScaleY = DecreaseScale(this.dScaleY);
558 }
559 public void ZoomOutX()
560 {
561 this.dScaleX = IncreaseScale(this.dScaleX);
562 }
563 public void ZoomOutY()
564 {
565 this.dScaleY = IncreaseScale(this.dScaleY);
566 }
567
568 public void ZoomIn()
569 {
570 ZoomInX();
571 ZoomInY();
572 }
573 public void ZoomOut()
574 {
575 ZoomOutX();
576 ZoomOutY();
577 }
578
579 #endregion
580
581 }
582 }
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Data;
5 using System.Drawing;
6 using System.Text;
7 using System.Windows.Forms;
8 using System.Drawing.Printing;
9
10 namespace VietGraph
11 {
12 public enum GraphMode
13 { Rectangular, Polar };
14
15 //[ToolboxBitmap("graph.bmp")]
16 public partial class ExpressionPlotter : Control
17 {
18
19 #region MemberVariables
20
21 int iLengthScale; //represents no. of pixels for a scale
22 int iOriginX, iOriginY; //represents the location of origin
23
24 double dScaleX = 10, dScaleY = 10; //base scale for graph
25 double dForwardX = 0, dForwardY = 0; //position related to base scale
26
27 int iDivisionsX = 5, iDivisionsY = 5;
28
29 int iLengthBox;
30 int iPrintStepX = 1;
31 int iPrintStepY = 1;
32 int iControlSize = 0;
33 int iPenWidth = 1;
34
35 bool bGrids = false;
36 bool bDisplayText = true;
37
38 int iPolarSensitivity = 100;
39
40 GraphMode graphMode = GraphMode.Rectangular;
41 PrintDocument printDoc;
42
43 List<IEvaluatable> expressions;
44 List<Color> expColors;
45 List<bool> expVisible;
46
47 #endregion
48
49 #region Control specific functions
50 public ExpressionPlotter()
51 {
52 this.expressions = new List<IEvaluatable>();
53 this.expColors = new List<Color>();
54 this.expVisible = new List<bool>();
55 InitializeComponent();
56 }
57
58 protected override void OnPaint(PaintEventArgs pe)
59 {
60 //update internal variables
61 UpdateVariables();
62
63 pe.Graphics.Clear(Color.White);
64 pe.Graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
65 pe.Graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
66 pe.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
67 PlotGraph(pe.Graphics);
68
69 // Calling the base class OnPaint
70 base.OnPaint(pe);
71 }
72
73 //private void ExpressionPlotter_Resize(object sender, EventArgs e)
74 //{
75 // //code to keep the control's height and width same
76
77 // //see whether width was changed or the height was changed
78 // if (iControlSize != this.Width)
79 // this.Height = this.Width; //width was changed so adjust height accordingly
80 // else
81 // this.Width = this.Height; //height was changed so adjust width accordingly
82 //}
83
84 private void UpdateVariables()
85 {
86 iControlSize = this.Height;
87 iLengthScale = (int)(iControlSize / 2.25);
88 iOriginX = (this.Width) / 2;
89 iOriginY = (this.Height) / 2;
90 if (bGrids == true)
91 this.iLengthBox = this.iLengthScale;
92 else
93 this.iLengthBox = (int)(this.iLengthScale * 0.025);
94 }
95 private double IncreaseScale(double scale)
96 {
97 double absScale = Math.Round(Math.Abs(scale), 3);
98 double newScale;
99 if (absScale >= 100)
100 newScale = (absScale + 100);
101 else if (absScale >= 10)
102 newScale = (absScale + 10);
103 else if (absScale >= 1)
104 newScale = (absScale + 1);
105 else if (absScale >= .10)
106 newScale = (absScale + .10);
107 else
108 newScale = (absScale + .010);
109 return newScale * Math.Sign(scale);
110 }
111 private double DecreaseScale(double scale)
112 {
113 double absScale = Math.Round(Math.Abs(scale), 3);
114 double newScale;
115 if (absScale > 100)
116 newScale = (absScale - 100);
117 else if (absScale > 10)
118 newScale = (absScale - 10);
119 else if (absScale > 1)
120 newScale = (absScale - 1);
121 else if (absScale > .1)
122 newScale = (absScale - .1);
123 else if (absScale > .01)
124 newScale = (absScale - .01);
125 else
126 newScale = absScale;
127 return newScale * Math.Sign(scale);
128 }
129
130 #endregion
131
132 #region Properties
133 public int DivisionsX
134 {
135 get { return this.iDivisionsX; }
136 set { if (value > 0) this.iDivisionsX = value; }
137 }
138 public int DivisionsY
139 {
140 get { return this.iDivisionsY; }
141 set { if (value > 0) this.iDivisionsY = value; }
142 }
143
144 public double ScaleX
145 {
146 get { return this.dScaleX; }
147 set { if (value != 0) this.dScaleX = value; }
148 }
149 public double ScaleY
150 {
151 get { return this.dScaleY; }
152 set { if (value != 0) this.dScaleY = value; }
153 }
154
155 public double ForwardX
156 {
157 get { return this.dForwardX; }
158 set { this.dForwardX = value; }
159 }
160 public double ForwardY
161 {
162 get { return this.dForwardY; }
163 set { this.dForwardY = value; }
164 }
165
166 public int PrintStepX
167 {
168 get { return this.iPrintStepX; }
169 set { if (value > 0) this.iPrintStepX = value; }
170 }
171 public int PrintStepY
172 {
173 get { return this.iPrintStepY; }
174 set { if (value > 0) this.iPrintStepY = value; }
175 }
176
177 public int PolarSensitivity
178 {
179 get { return this.iPolarSensitivity; }
180 set { if (value > 0) this.iPolarSensitivity = value; }
181 }
182
183 public bool Grids
184 {
185 get { return this.bGrids; }
186 set { this.bGrids = value; }
187 }
188
189 public bool DisplayText
190 {
191 get { return bDisplayText; }
192 set { bDisplayText = value; }
193 }
194
195 public GraphMode GraphMode
196 {
197 get { return graphMode; }
198 set { graphMode = value; }
199 }
200
201 public int PenWidth
202 {
203 get { return iPenWidth; }
204 set { iPenWidth = value; }
205 }
206
207 #endregion
208
209 #region Plotting Functions
210 void PlotGraph(Graphics g)
211 {
212 DisplayScale(g);
213 if (this.bDisplayText)
214 DisplayExpressionsText(g);
215
216 double X, Y;
217 double dPointX, dPointY;
218 double dLeastStepX, dLeastStepY;
219 double dMin, dMax, dStep;
220 int i;
221
222 //(X1,Y1) is the previous point ploted, (X2,Y2) is the current point to plot. (we will join both to have our
223 // graph continuous).
224 float X1 = 0, Y1 = 0, X2 = 0, Y2 = 0;
225 //This variable controls whether our graph should be continuous or not
226 bool bContinuity = false;
227
228 //divide scale with its length(pixels) to get increment per pixel
229 dLeastStepX = dScaleX / iLengthScale;
230 dLeastStepY = dScaleY / iLengthScale;
231
232 //prepare variables for loop
233 if (graphMode == GraphMode.Polar)
234 {
235 dMin = -Math.PI;
236 dMax = Math.PI;
237 dStep = dScaleX / iPolarSensitivity;
238 }
239 else //if (Rectangular Mode)
240 {
241 dStep = dLeastStepX;
242 dMin = -dScaleX + dForwardX;
243 dMax = dScaleX + dForwardX;
244 }
245
246
247 for (i = 0; i < this.expressions.Count; i++)
248 {
249 //check if expression needs to be drawn and is valid
250 if (expVisible[i] == true && expressions[i].IsValid == true)
251 {
252 bContinuity = false;
253 for (X = dMin; X != dMax; X += dStep)
254 {
255 if (dScaleX < 0 && X < dMax)
256 break;
257 if (dScaleX > 0 && X > dMax)
258 break;
259 try
260 {
261 //evaluate expression[i] at point: X
262 Y = expressions[i].Evaluate(X);
263 if (double.IsNaN(Y))
264 {
265 //break continuity in graph if expression returned a NaN
266 bContinuity = false;
267 continue;
268 }
269
270 //get points to plot
271 if (graphMode == GraphMode.Polar)
272 {
273 dPointX = Y * Math.Cos(X) / dLeastStepX;
274 dPointY = Y * Math.Sin(X) / dLeastStepY;
275 }
276 else // if (Rectangular mode;
277 {
278 dPointX = X / dLeastStepX;
279 dPointY = Y / dLeastStepY;
280 }
281
282 //check if the point to be plotted lies inside our visible area(i.e. inside our current axes ranges)
283 if ((iOriginY - dPointY + dForwardY / dLeastStepY) < iOriginY - iLengthScale
284 || (iOriginY - dPointY + dForwardY / dLeastStepY) > iOriginY + iLengthScale
285 || (iOriginX + dPointX - dForwardX / dLeastStepX) < iOriginX - iLengthScale
286 || (iOriginX + dPointX - dForwardX / dLeastStepX) > iOriginX + iLengthScale)
287 {
288 //the point lies outside our current scale so break continuity
289 bContinuity = false;
290 continue;
291 }
292
293 //get coordinates for currently evaluated point
294 X2 = (float)(iOriginX + dPointX - dForwardX / dLeastStepX);
295 Y2 = (float)(iOriginY - dPointY + dForwardY / dLeastStepY);
296
297 //if graph should not be continuous
298 if (bContinuity == false)
299 {
300 X1 = X2;
301 Y1 = Y2;
302 // the graph should be continuous afterwards since the current evaluated value is valid
303 // and can be plot within our axes range
304 bContinuity = true;
305
306 }
307
308 //join points (X1,Y1) and (X2,Y2)
309 g.DrawLine(new Pen(expColors[i], iPenWidth), new PointF(X1, Y1), new PointF(X2, Y2));
310
311 //get current values into X1,Y1
312 X1 = X2;
313 Y1 = Y2;
314 }
315 catch
316 {
317 bContinuity = false;
318 continue;
319 }
320
321 }
322 }
323 }
324 }
325
326 void DisplayScale(Graphics g)
327 {
328 //axes lines
329 g.DrawLine(new Pen(Color.Black, 2),
330 new Point(iOriginX - iLengthScale, iOriginY),
331 new Point(iOriginX + iLengthScale, iOriginY));
332
333 g.DrawLine(new Pen(Color.Black, 2),
334 new Point(iOriginX, iOriginY - iLengthScale),
335 new Point(iOriginX, iOriginY + iLengthScale));
336
337 int i;
338 double dValue;
339 string strValue;
340
341 float cordX, cordY;
342
343 //X-axis values
344 dValue = -dScaleX + dForwardX;
345 for (i = -iDivisionsX; i <= iDivisionsX; i++)
346 {
347 g.DrawLine(new Pen(Color.Gray, 1),
348 new PointF((float)(iOriginX + (dValue - dForwardX) * iLengthScale / dScaleX), iOriginY - iLengthBox),
349 new PointF((float)(iOriginX + (dValue - dForwardX) * iLengthScale / dScaleX), iOriginY + iLengthBox));
350 if (i % iPrintStepX == 0 && i != 0)
351 {
352 strValue = Math.Round(dValue, 3).ToString();
353 cordX = (float)(iOriginX + (dValue - dForwardX) * iLengthScale / dScaleX - 6 - (strValue.Length - 2) * 5);
354 cordY = (float)(iOriginY + 10);
355 g.DrawString(strValue, new Font("Arial", 8), new SolidBrush(Color.Black), cordX, cordY);
356 }
357 dValue = dValue + dScaleX / iDivisionsX;
358 }
359
360
361 //Y-axis values
362 dValue = -dScaleY + dForwardY;
363 for (i = -iDivisionsY; i <= iDivisionsY; i++)
364 {
365 g.DrawLine(new Pen(Color.Gray, 1),
366 new PointF(iOriginX - iLengthBox, (float)(iOriginY + (dValue - dForwardY) * iLengthScale / dScaleY)),
367 new PointF(iOriginX + iLengthBox, (float)(iOriginY + (dValue - dForwardY) * iLengthScale / dScaleY)));
368 if (i % iPrintStepY == 0 && i != 0)
369 {
370 strValue = Math.Round(dValue, 3).ToString();
371 cordX = (float)(iOriginX - 20 - (strValue.Length) * 4);
372 cordY = (float)(iOriginY - (dValue - dForwardY) * iLengthScale / dScaleY - 7);
373 if (this.iLengthBox == this.iLengthScale)
374 cordY += 6;
375 g.DrawString(strValue, new Font("Arial", 8), new SolidBrush(Color.Black), cordX, cordY);
376 }
377 dValue = dValue + dScaleY / iDivisionsY;
378 }
379
380 if (graphMode == GraphMode.Polar)
381 {
382 g.DrawEllipse(new Pen(Color.Black, 1), iOriginX - iLengthScale, iOriginY - (float)(iLengthScale * dScaleX / dScaleY), iLengthScale*2, (float)(iLengthScale * dScaleX / dScaleY)*2);
383 for (dValue = 0; dValue <= 2 * Math.PI; dValue += Math.PI / 6)
384 {
385 g.DrawLine(new Pen(Color.Black, 1), new Point(iOriginX, iOriginY),
386 new PointF((float)(iOriginX + iLengthScale * Math.Cos(dValue)), (float)(iOriginY + iLengthScale * Math.Sin(dValue))));
387 }
388 }
389 }
390
391 void DisplayExpressionsText(Graphics g)
392 {
393 int line = 0;
394 for (int i = 0; i < this.expressions.Count; i++)
395 {
396 if (expVisible[i] == true)
397 {
398 if (expressions[i].IsValid)
399 g.DrawString(expressions[i].ExpressionText, new Font("Arial", 10), new SolidBrush(expColors[i]), 10, 10 + 18 * line);
400 else
401 g.DrawString("ERROR: " + expressions[i].ExpressionText, new Font("Arial", 10), new SolidBrush(expColors[i]), 10, 10 + 18 * line);
402 line++;
403 }
404 }
405 }
406
407 void InitializePrintDoc()
408 {
409 this.printDoc = new PrintDocument();
410 this.printDoc.OriginAtMargins = true;
411 this.printDoc.DefaultPageSettings.Margins.Left = 200;
412 this.printDoc.DefaultPageSettings.Margins.Top = 100;
413 this.printDoc.DocumentName = "Graph Plotter by Syed Mehroz Alam";
414 this.printDoc.PrintPage += delegate(object sender, PrintPageEventArgs e) { PlotGraph(e.Graphics); };
415 }
416
417 #endregion
418
419 #region Public functions for expression management
420
421 public void AddExpression(IEvaluatable expression, Color color, bool visible)
422 {
423 expressions.Add(expression);
424 expColors.Add(color);
425 expVisible.Add(visible);
426 }
427 public bool RemoveExpression(IEvaluatable expression)
428 {
429 int index = expressions.IndexOf(expression);
430 if (index == -1)
431 return false;
432 expressions.RemoveAt(index);
433 expColors.RemoveAt(index);
434 expVisible.RemoveAt(index);
435 return true;
436 }
437 public void RemoveExpressionAt(int index)
438 {
439 // can throw OutOfRangeException
440 expressions.RemoveAt(index);
441 expColors.RemoveAt(index);
442 expVisible.RemoveAt(index);
443 }
444 public void RemoveAllExpressions()
445 {
446 this.expressions.Clear();
447 this.expColors.Clear();
448 this.expVisible.Clear();
449 }
450
451 public IEvaluatable GetExpression(int index)
452 {
453 // can throw OutOfRangeException
454 return expressions[index];
455 }
456 public Color GetExpressionColor(int index)
457 {
458 // can throw OutOfRangeException
459 return expColors[index];
460 }
461 public bool GetExpressionVisibility(int index)
462 {
463 // can throw OutOfRangeException
464 return expVisible[index];
465 }
466
467 public void SetExpression(int index, IEvaluatable expression)
468 {
469 // can throw OutOfRangeException
470 this.expressions[index]=expression;
471 }
472 public void SetExpressionColor(int index, Color color)
473 {
474 // can throw OutOfRangeException
475 this.expColors[index]=color;
476 }
477 public void SetExpressionVisibility(int index, bool visibility)
478 {
479 // can throw OutOfRangeException
480 this.expVisible[index]=visibility;
481 }
482
483 #endregion
484
485 #region Public functions for graph management
486
487 public void SetRangeX(double startX, double endX)
488 {
489 this.dScaleX = (endX - startX) / 2;
490 this.dForwardX = (endX + startX) / 2;
491 }
492 public void SetRangeY(double startY, double endY)
493 {
494 this.dScaleY = (endY - startY) / 2;
495 this.dForwardY = (endY + startY) / 2;
496 }
497
498 public void RestoreDefaults()
499 {
500 this.dScaleX = this.dScaleY = 10;
501 this.dForwardX = this.dForwardY = 0;
502 this.iDivisionsX=this.iDivisionsY = 5;
503 this.iPrintStepX = this.iPrintStepY = 1;
504 this.bGrids = false;
505 this.iPolarSensitivity = 100;
506 }
507
508 public void ToggleGrids()
509 {
510 this.bGrids = (!bGrids);
511 }
512
513 public double[] GetValues(double x)
514 {
515 double[] result = new double[expressions.Count];
516 for (int i = 0; i < this.expressions.Count; i++)
517 if (this.expressions[i].IsValid)
518 result[i] = this.expressions[i].Evaluate(x);
519 return result;
520 }
521
522 public Bitmap GetGraphBitmap()
523 {
524 Bitmap bmpSnap = new Bitmap(this.Width, this.Height);
525 DrawToBitmap(bmpSnap, new Rectangle(0, 0, this.Width, this.Height));
526 return bmpSnap;
527 }
528
529 public void CopyToClipboard()
530 {
531 Clipboard.SetImage(GetGraphBitmap());
532 }
533
534 public void MoveLeft(int divisions)
535 {
536 this.dForwardX -= divisions * this.dScaleX / this.iDivisionsX;
537 }
538 public void MoveRight(int divisions)
539 {
540 this.dForwardX += divisions * this.dScaleX / this.iDivisionsX;
541 }
542 public void MoveUp(int divisions)
543 {
544 this.dForwardY += divisions * this.dScaleY / this.iDivisionsY;
545 }
546 public void MoveDown(int divisions)
547 {
548 this.dForwardY -= divisions * this.dScaleY / this.iDivisionsY;
549 }
550
551 public void ZoomInX()
552 {
553 this.dScaleX = DecreaseScale(this.dScaleX);
554 }
555 public void ZoomInY()
556 {
557 this.dScaleY = DecreaseScale(this.dScaleY);
558 }
559 public void ZoomOutX()
560 {
561 this.dScaleX = IncreaseScale(this.dScaleX);
562 }
563 public void ZoomOutY()
564 {
565 this.dScaleY = IncreaseScale(this.dScaleY);
566 }
567
568 public void ZoomIn()
569 {
570 ZoomInX();
571 ZoomInY();
572 }
573 public void ZoomOut()
574 {
575 ZoomOutX();
576 ZoomOutY();
577 }
578
579 #endregion
580
581 }
582 }